#!/usr/bin/env python3
# Copyright 2024 The Lynx Authors. All rights reserved.
# Licensed under the Apache License Version 2.0 that can be found in the
# LICENSE file in the root directory of this source tree.

import io
import os
import subprocess
import sys
import tempfile

LICENSE = """// Copyright 2024 The Lynx Authors. All rights reserved.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.

//
// DO NOT MODIFY THIS FILE! AUTO GENERATED BY clay/tools/make_keywords.py
//
"""

H_TEMPLATE = """%(license)s
#ifndef CLAY_UI_COMPONENT_KEYWORDS_H_
#define CLAY_UI_COMPONENT_KEYWORDS_H_

#include <string>

namespace clay {

enum class KeywordID {
  kInvalid = -1,
  %(id_list)s
};

KeywordID GetKeywordID(const std::string &str);

}  // namespace clay

#endif  // CLAY_UI_COMPONENT_KEYWORDS_H_
"""

GPERF_TEMPLATE = """%%language=C++
%%define class-name KeywordHash
%%define lookup-function-name find
%%define string-pool-name StringPool
%%struct-type
%%global-table
%%readonly-tables
%%pic

%%{
%(license)s
#include "clay/ui/component/keywords.h"

#include <string>

namespace clay {
%%}

struct TokenValue {
  short name;
  short id;
};

%%%%
%(str2id_list)s
%%%%

static_assert(sizeof(StringPool_contents) < 32767);

KeywordID GetKeywordID(const std::string& str) {
  auto *tk = KeywordHash::find(str.c_str(), str.size());
  return tk ? static_cast<KeywordID>(tk->id) : KeywordID::kInvalid;
}

}  // namespace clay
"""

def camelcase_name(name):
    return ''.join(w.capitalize() for w in name.split('-'))

def main():
    base_dir = os.path.join('oss','clay', 'ui', 'component')
    keywords_in = os.path.join(base_dir, 'keywords.in')
    keywords_h = os.path.join(base_dir, 'keywords.h')
    keywords_cc = os.path.join(base_dir, 'keywords.cc')
    kw_list = []
    id_list = []
    str2id_list = []
    f = io.open(keywords_in, 'r+', encoding='utf-8')
    for line in f.readlines():
        token = line.strip()
        if token.startswith('//'):
            continue
        enum = 'k' + camelcase_name(token)
        kw_list.append(token)
        id_list.append(enum)
        str2id_list.append(token + ', (short)KeywordID::' + enum)
    f.close()

    # generate keywords.h
    f = io.open(keywords_h, 'w', encoding='utf-8')
    f.write(H_TEMPLATE % { 'license': LICENSE, 'id_list': ',\n  '.join(id_list)})
    f.close()

    # generate keywords.gperf
    tmp = tempfile.NamedTemporaryFile(delete=False, mode="wt", encoding='utf-8')
    keywords_gperf = tmp.name
    tmp.write(GPERF_TEMPLATE % { 'license': LICENSE, 'str2id_list': '\n'.join(str2id_list)})
    tmp.close()

    # generate keywords.cc
    subprocess.check_call(['gperf', keywords_gperf, '-m', '100', '--output-file=' + keywords_cc])

    ## post processes
    lines = []
    f = io.open(keywords_cc, 'r+', encoding='utf-8')
    for line in f.readlines():
        if line.startswith('#line') or line.startswith('/* Command-line:'):
            continue
        line = line.replace('(int)', '(short)').replace('register ', '')
        lines.append(line)
    f.close()

    ## write back
    f = io.open(keywords_cc, 'w', encoding='utf-8')
    f.write(''.join(lines))
    f.close()

if __name__ == "__main__":
    main()
